/*
  * Copyright (c) 2016, www.vnetoo.com. All rights reserved.
  */
package com.example.Aspect;

import com.example.annotation.DoValid;
import com.example.vo.ResultVO;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.groups.Default;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Set;

/**
 * 表单后台验证切面
 *
 * @date 2016年11月25日 上午10:12:21
 * @author zhaoj
 * @since V2.0.0
 */
@Aspect
@Component
public class DoValidAspect {

    @Autowired
    private LocalValidatorFactoryBean localValidator;


    /**
     * 验证类的结尾
     *
     * @date 2016年11月25日 下午4:51:41
     * @author zhaoj
     * @since V2.0.0
     */
    public static final String VALIDATOR_SUFFIX = "Validator";

    /**
     * 定义切面
     *
     * @date 2016年11月25日 上午10:12:02
     * @author zhaoj
     * @since V2.0.0
     */
    @Pointcut("execution(public * com.example.controller.*.*(..))")
    public void validate() {
    }

    /**
     * 切面方法体环绕
     *
     * @date 2016年11月25日 上午10:11:33
     * @author zhaoj
     * @since V2.0.0
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "validate()")
    private Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
         /*
          * 算法： 1.取得方法体中的参数注解二维数组 2.逐一遍历参数中的注解，并对带有带有 @Valid 注解的参数做验证处理
          * 
          * 验证处理： 1.调用当前Controller中的validate()
          * 2.如果返回的CommonResponse.Result!=Result.Success，验证返回类型 
          * 2.1 JSON(主要用于添加、编辑)：判断Controller是否有@RestController 或者 方法上加了@ResponseBody，返回带错误信息的JSON
          * 2.2 View(主要用于查询)：        判断Controller是否返回值为ModelAndView，返回带错误信息的错误页面
          * 
          * 其中，错误信息在CommonResponse.msg中
          * 3.若所有验证都通过了，则可以继续执行方法
          */
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        //[参数][注解]二维数组
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        if (parameterAnnotations != null && parameterAnnotations.length != 0) {
            for (int i = 0; i != parameterAnnotations.length; ++i) {
                Annotation[] param = parameterAnnotations[i];
                for (int j = 0; j != param.length; ++j) {
                    Annotation annotation = param[j];
                    if (annotation instanceof DoValid) {
                        // 参数中有验证注解
                        Object bo = pjp.getArgs()[i];

                        /*******************单个入参验证开始*******************/
                        ResultVO validator = validate(bo);
                        //分组支持
                        //CommonResponse validator = validate(bo,((DoValid) annotation).value());
                        if (validator.getCode() != 1) {
                            //验证不通过,验证返回方式
                            if(isJsonResponse(pjp)){
                                //返回JSON
                                //return new Re(validator .getResult(), AppContext.token(), validator.getMsg());
                            }else{
                                //返回View
                                return new ModelAndView("errorMsg").addObject("errMsg", validator .getMsg());
                            }
                        }
                        /*******************单个入参验证结束*******************/
                    }
                }
            }
        }

        //所有入参验证完毕、已做字段过滤，且均合法
        //代码若能执行到此处，则说明验证通过，则执行Controller层中的方法体
        return pjp.proceed();
    }

    /**
     * 参数验证
     * @date 2016年12月1日 下午4:46:00
     * @author zhaoj
     * @since V2.0.0
     * @param bo
     * @return
     */
    public  ResultVO validate(Object bo) {
        Set<ConstraintViolation<Object>> set = localValidator.getValidator().validate(bo,Default.class);
        //分组验证支持
        //Set<ConstraintViolation<Object>> set2 = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo, ((DoValid) annotation).value());
        if (set != null && !set.isEmpty()) {
            String msg = "";

            Iterator<ConstraintViolation<Object>> iterator = set.iterator();
            while (iterator.hasNext()) {
                String singleMsg = iterator.next().getMessage();
                if (StringUtils.isNotEmpty(singleMsg)) {
                    msg += singleMsg + ",";
                }
            }

            if (StringUtils.isNotEmpty(msg.toString())) {
                msg = msg.substring(0, msg.toString().length() - 1);
            }

            return new ResultVO(-1, msg);
        }

        return new ResultVO(1, null);
    }

    /**
     * 判断是否是返回JSON的请求
     * @date 2016年12月1日 下午4:45:50
     * @author zhaoj
     * @since V2.0.0
     * @param pjp
     * @return
     */
    public static boolean isJsonResponse(ProceedingJoinPoint pjp){
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Annotation[] methodAnnotations = method.getAnnotations();
        for(Annotation tmp : methodAnnotations){
            if(tmp instanceof ResponseBody){
                return true;
            }
        }

        Annotation[] classAnnotations = method.getClass().getAnnotations();
        for(Annotation tmp : classAnnotations){
            if(tmp instanceof RestController){
                return true;
            }
        }

        return false;
    }



   /* public void MethodArgumentNotValidException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
        logger.error( ":" + CommonUtil.getHttpClientInfo(request), ex);
        MethodArgumentNotValidException c = (MethodArgumentNotValidException) ex;
        List<ObjectError> errors =c.getBindingResult().getAllErrors();
        StringBuffer errorMsg=new StringBuffer();
        errors.stream().forEach(x -> errorMsg.append(x.getDefaultMessage()).append(";"));
        pouplateExceptionResponse(response, HttpStatus.INTERNAL_SERVER_ERROR, errorMsg.toString());
    }


    private void pouplateExceptionResponse(HttpServletResponse response, HttpStatus errorCode, String errorMessage) {
        try {
            response.sendError(errorCode.value(), errorMessage);
        } catch (IOException e) {
            logger.error("failed to populate response error", e);
        }
    }*/

}
